=begin

Next:
* table links
* column descriptions
* improving view-access of UIObject (automatic view assignment? thread variable?)
* more link config
  confirm
  opening behavior
    popin - response is html. this means taking response and injecting into redbox (or similar), and configuring a close control.
      options?
    ajax - response is html. this means taking response and wrapping it in an adapter to insert somewhere.
      should i do this inline like activescaffold? or have a "presentation window" that everything displays in?
    rjs - response is custom rjs. just evaluate response.
  closing behavior
    refresh page
    reload part of page (aka execute custom javascript)
* column data type intelligence
  based on semantic attributes? or on activerecord column connection object? both somewhat dissatisfying.
  types
    date
    number (alignment)
    boolean
    - extensible -
    array (create a list)

=end
class Present::List < Present::UIObject
  # the columns to display
  def columns
    @columns ||= []
  end
  def columns=(val)
    @columns = case val
    when Hash
      Column.collection_from_hash(val)
    when Array
      Column.collection_from_array(val)
    else
      val
    end
  end

  # the title of this list
  attr_accessor :title

  # the name of this list - not displayed to user, used to reference this list programmatically
  attr_accessor :name

  # per-record links
  attr_writer :record_links
  def record_links
    @record_links ||= []
  end

  # whether to render the list with the assumption that there are other lists on the same page
  # currently this means:
  # * namespace the params using self.name
  attr_accessor :multi_mode

  def render(pager_or_items)
    pager, items, current_page = nil, nil, nil
    if pager_or_items.is_a?(Paginator)
      page_number = params[param_name(:page)] || 1
      pager = pager_or_items
      current_page = pager.page(page_number)
      items = current_page.items
    else
      items = pager_or_items
    end

    content_tag 'div', :id => "#{name}_list", :class => "present present_list" do
      content_tag 'table', :cellspacing => '0' do
        render_thead + render_tbody(items) + render_tfoot(pager, current_page)
      end
    end
  end

  protected

  def render_thead
    content_tag 'thead' do
      render_title_row + render_column_headers
    end
  end

  def render_title_row
    content_tag 'tr' do
      content_tag 'th', :colspan => cell_width_of_table do
        content_tag 'h1', title
      end
    end
  end

  def render_column_headers
    content_tag 'tr' do
      cells = columns.collect { |column|
        content_tag 'th', render_column_header(column)
      }.join
      cells << content_tag('th', '') if record_links
      cells
    end
  end

  def render_column_header(column)
    if column.sortable
      sort_param = param_name("sort_#{column.name}")
      params = {sort_param => column.sorted ? 'DESC' : 'ASC'}
      link_to(column.label, params)
    else
      column.label
    end
  end

  def render_tbody(items)
    items.collect { |record|
      tr_class = cycle('odd', 'even')
      tr_class += ' first' if record == items.first
      tr_class += ' last' if record == items.last
      content_tag 'tr', :class => tr_class do
        cells = columns.collect { |column|
          td_class = ''
          td_class += ' first' if column == columns.first
          td_class += ' last' unless record_links or column != columns.last
          content_tag 'td', column.value_for(@view, record), :class => td_class
        }.join
        cells << content_tag('td', render_record_links(record), :class => 'actions last') if record_links
        cells
      end
    }.join
  end

  # TODO:
  # * ajax links
  # * skip to specific page
  def render_tfoot(pager, current_page)
    content_tag 'tfoot' do
      content_tag 'tr' do
        content_tag 'td', :colspan => cell_width_of_table do
          pager ? render_pagination(pager, current_page) : content_tag('p', "#{current_page.size} records")
        end
      end
    end
  end

  def render_pagination(pager, current_page)
    pages = {
      :first => ["&#171;",  '#'],  # <<
      :prev =>  ["&#8249;", '#'],  #  <
      :next =>  ["&#8250;", '#'],  #  >
      :last =>  ["&#187;",  '#']   # >>
    }
    pages[:first][1] = {param_name(:page) => 1}                        if current_page.number != 1
    pages[:prev][1]  = {param_name(:page) => current_page.prev.number} if current_page.prev?
    pages[:next][1]  = {param_name(:page) => current_page.next.number} if current_page.next?
    pages[:last][1]  = {param_name(:page) => pager.last.number}        if current_page.number != pager.last.number

    paging = content_tag 'ol' do
      [:first, :prev, :last, :next].collect {|k| content_tag('li', link_to(*pages[k]), :class => k)}.join
    end

    current = content_tag "p", "page #{current_page.number} of #{pager.number_of_pages}"

    paging + current
  end

  def render_record_links(record)
    content_tag 'ul' do
      record_links.collect { |link|
        link.view = @view
        content_tag('li', link.render(record))
      }.join
    end
  end

  def cell_width_of_table
    w = columns.size
    w += 1 if record_links
    w
  end

  def param_name(name)
    self.multi_mode ? "#{self.name}_#{name}".to_sym : name.to_sym
  end

  # extra options for the List column
  class Column < Present::Column
    # TODO: appears in a tooltop over the column title
    attr_accessor :description

    # boolean for whether the column is sortable
    attr_accessor :sortable

    # boolean for whether the column is currently sorted
    attr_accessor :sorted
  end
end
